package engine.res;

import engine.render.Shader;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.TreeMap;
import javax.imageio.ImageIO;
import org.lwjgl.BufferUtils;
import util.GLUtil;
import util.Util;
import static engine.GL.*;

public class Res 
{

    private static Res loader;
    
    public static Res loader() // Singleton getter
    {
        if (loader == null) 
        {
            loader = new Res();
        }
        return loader;
    }
    
    private int nTextures;
    public int getNumTextures() 
    {
        return nTextures;
    }
    
    private TreeMap<String, Bitmap> texMap;
    private TreeMap<String, Bitmap[]> animMap;
    private TreeMap<String, Shader> shaderMap;
    private Bitmap texDefault;

    private Res() 
    {
        nTextures = 0;
        texDefault = this.genTexture(new byte[] { (byte)255, 0, (byte)255, (byte)255 }, 1, 1);
        texMap = new TreeMap<String, Bitmap>();
        animMap = new TreeMap<String, Bitmap[]>();
        shaderMap = new TreeMap<String, Shader>();
    }
    
    public void load() 
    {
        String fileName;
        String[] fileNames = new File("imgs/").list();
        for (int i = 0; i < fileNames.length; i++) 
        {
            if (fileNames[i].endsWith(".png") || fileNames[i].endsWith(".PNG")) 
            {
                fileName = "imgs/" + fileNames[i].substring(0, fileNames[i].indexOf("."));
                if (!texMap.containsKey(fileName)) 
                {
                    this.loadTexture(fileName);
                }
            }
        }
        fileNames = new File("shaders/").list();
        for (int i = 0; i < fileNames.length; i++) 
        {
            if (fileNames[i].endsWith(".vert") || fileNames[i].endsWith(".frag")) 
            {
                fileName = "shaders/" + fileNames[i].substring(0, fileNames[i].indexOf("."));
                if (!shaderMap.containsKey(fileName)) 
                {
                    Shader shader = new Shader(fileName);
                    shaderMap.put(fileName, shader);
                }
            }
        }
    }
    
    public Bitmap getTex(String texName) {
        if (texMap.containsKey("imgs/" + texName)) 
        {
            return texMap.get("imgs/" + texName);
        } 
        else 
        {
            return texDefault; // Default return in case of failure
        }
    }
    
    public Bitmap[] getAnimation(String animName) {
        if (animMap.containsKey("imgs/animations/" + animName)) 
        {
            return animMap.get("imgs/animations/" + animName);
        } 
        else 
        {
            return new Bitmap[] { texDefault }; // Default return in case of failure
        }
    }

    public Shader getShader(String shaderName) {
        if (shaderMap.containsKey("shaders/" + shaderName)) 
        {
            return shaderMap.get("shaders/" + shaderName);
        } 
        else 
        {
            return null; // Default return in case of failure
        }
    }
    
    private byte[] getByteArr(BufferedImage img, int startX, int texsW, int texsH) 
    {
        int pix, alpha, r, g, b;
        int index = 0;
        byte[] byteArr = new byte[texsH * texsW * 4];
        for (int y = texsH - 1; y >= 0; y--) // Iterations = (texW * texH), huge cost
        {
            for (int x = startX; x < (startX + texsW); x++) 
            {
                pix = img.getRGB(x, y); // Grab pixel data from the image
                alpha = (pix >> 24) & 0xFF; // Unpack pixel data from ARGB format
                r = (pix >> 16) & 0xFF;
                g = (pix >> 8) & 0xFF;
                b = pix & 0xFF;
                byteArr[index++] = (byte) r; // Pack pixel data into RGBA format
                byteArr[index++] = (byte) g;
                byteArr[index++] = (byte) b;
                byteArr[index++] = (byte) alpha;
            }
        }
        return byteArr;
    }
    
    public BufferedImage loadImage(String file) 
    {
        BufferedImage img = null;
        try 
        {
            img = ImageIO.read(new File(file + ".png"));
        } 
        catch (Exception e) 
        {
            Util.console("Failure loading image: " + file);
            Util.console(e);
            Util.quit();
        }
        return img;
    }
    
    private void loadTexture(String file) 
    {
        BufferedImage img = this.loadImage(file);
        int texW = img.getWidth();
        int texH = img.getHeight();
        byte[] byteArr = this.getByteArr(img, 0, texW, texH);
        Bitmap result = this.genTexture(byteArr, texW, texH);
        texMap.put(file, result);
    }
    
    private Bitmap genTexture(byte[] byteArr, int texsW, int texsH) 
    {
        ByteBuffer texImageBuffer;
        IntBuffer texName = BufferUtils.createIntBuffer(1);
        texImageBuffer = Util.getByteBuf(byteArr);

        pixelStoreMode(UNPACK_ALIGNMENT, 1);
        genTextures(texName);
        bindTexture(texName.get(0));

        texParam(TEXTURE_MIN_FILTER, LINEAR_MIPMAP_LINEAR);
        texParam(TEXTURE_MAG_FILTER, NEAREST);
        texParam(TEXTURE_ANISOTROPY, GLUtil.getAnisotropyCapacity());
        texParam(TEXTURE_LOD_BIAS, GLUtil.getLODCapacity());
//        texParam(TEXTURE_WRAP_S, REPEAT);
//        texParam(TEXTURE_WRAP_T, REPEAT);
        texParam(TEXTURE_WRAP_S, CLAMP_TO_EDGE);
        texParam(TEXTURE_WRAP_T, CLAMP_TO_EDGE);
        texParam(GENERATE_MIPMAP, TRUE);
        createTexImage2D(texsW, texsH, texImageBuffer);
//        genMipMaps();
        return new Bitmap(texName.get(0), nTextures++, texsW, texsH, texImageBuffer);
    }
    
    private void loadAnimation(String file, int nFrames) 
    {
        BufferedImage img = this.loadImage(file);
        BufferedImage[] imgFrames = new BufferedImage[nFrames];
        Bitmap[] texFrames = new Bitmap[nFrames];
        int w = img.getWidth(); // 700
        int h = img.getHeight(); // 70
        float deltaWf = w / nFrames; // 70
        int deltaWi = (int) deltaWf;
        int leftXi;
        byte[] byteArr;
        for (int i = 0; i < nFrames; i++) 
        {
            leftXi = (int)(i * deltaWf);
            imgFrames[i] = img.getSubimage(leftXi, 0, deltaWi, h);
            byteArr = this.getByteArr(imgFrames[i], 0, deltaWi, h);
            texFrames[i] = this.genTexture(byteArr, deltaWi, h);
        }
        animMap.put(file, texFrames);
    }

    public Bitmap[] tryLoadAnimation(String fileName, int nFrames) 
    {
        String file = "imgs/animations/" + fileName;
        if (!animMap.containsKey(file)) 
        {
            this.loadAnimation(file, nFrames);
        }
        return this.getAnimation(fileName);
    }

}
